/*
 * Copyright (c) 2006-2022, xxxx Development Team
 *
 *
 * Change Logs:
 * Date           Author       Version      Notes
 * 2022-11-22     TSL          v0.1         first version
 */
 
/* 头文件 ---------------------------------------------------------------------*/
#include "fsl_ioctl.h"
#include <stddef.h>

/* 宏定义 ---------------------------------------------------------------------*/
#define IOCTL_STD_OFF   0
#define IOCTL_STD_ON    1
#define IOCTL_SET_ON(_dev_)   _dev_->on(); dev->state = IOCTL_STD_ON
#define IOCTL_SET_OFF(_dev_)  _dev_->off(); dev->state = IOCTL_STD_OFF

#define GET_TICK()        s_tick_ms

#if FSL_IOCTL_LOOP_CALL_MODE == FSL_IOCTL_LOOP_GAP_TICK
#define UPDATE_TICK(_tick_) s_tick_ms += _tick_
#elif FSL_IOCTL_LOOP_CALL_MODE == FSL_IOCTL_LOOP_SYS_TICK
#define UPDATE_TICK(_tick_) s_tick_ms = _tick_
#endif

#define LOOP_PERIOD_MS  10

/* 类型定义 -------------------------------------------------------------------*/

/* 函数声明 -------------------------------------------------------------------*/

/* 变量声明 -------------------------------------------------------------------*/
static uint32_t s_tick_ms = 0;
static LIST_HEAD(dev_list_head);

/*
 * 函数说明
 */
void fsl_ioctl_init_dev(ioctl_dev_t *dev)
{
    dev->state = IOCTL_STD_OFF;
    dev-> repeat_cnt = 0;
    dev-> repeat_cfg = 0;
    dev-> on_time_cfg = 0;
    dev-> off_time_cfg = 0;
    dev-> start_tick = 0;
    dev->on = NULL;
    dev->off = NULL;
    list_init(&dev->list);
}

/*
 * 函数说明
 */
void fsl_ioctl_register_dev(ioctl_dev_t *dev)
{
    if ((dev->on == NULL) || (dev->off == NULL))
    {
        return;
    }
    
    list_insert_tail(&dev_list_head, &dev->list);
}

/*
 * 函数说明
 * on_time_ms: 点亮时间
 * off_time_ms: 熄灭时间
 * repeat_num: 执行次数
 * 说明：
 * （1）无限次数闪烁：on_time_ms > 0；off_time_ms > 0; repeat_num = 0xFF
 * （2）常亮：on_time_ms > 0; off_time_ms = 0; repeat_num = 0xFF
 * （3）常熄灭：on_time_ms = 0; off_time_ms ≥ 0; repeat_num = 0xFF
 * （4）若repeat_num为0，则忽略
 */
void fsl_ioctl_setup(ioctl_dev_t *dev, uint16_t on_time_ms, uint16_t off_time_ms, uint8_t repeat_num)
{
    // 参数合法性检查
    if (repeat_num == 0) // || (dev == NULL)
    {
        return;
    }

    // 当前实例正在运行，则判断参数是否变化，无变化则返回
    if (dev->repeat_cnt > 0)
    {
        if ((on_time_ms == dev->on_time_cfg) && \
            (off_time_ms == dev->off_time_cfg) && \
            (repeat_num == dev->repeat_cfg))
        {
            return; //参数相同不更新
        }
    }

    // 更新参数配置
    dev->on_time_cfg = on_time_ms;
    dev->off_time_cfg = off_time_ms;
    dev->repeat_cfg = repeat_num;
    dev->repeat_cnt = repeat_num;

    // 特殊时间长度处理
    if(dev->on_time_cfg == 0)
    {
       dev->repeat_cnt = 0;
    }
    else if(dev->off_time_cfg == 0)
    {
        if(dev->repeat_cnt == 0xff) //常亮
        {
            dev->repeat_cnt = 0;
        }
        else
        {
            dev->on_time_cfg *= dev->repeat_cnt;
            dev->repeat_cnt = 1;
        }
    }

    // 指示灯状态控制
    dev->start_tick = GET_TICK();
    if (dev->on_time_cfg)
    {
        IOCTL_SET_ON(dev);
    }
    else
    {
        IOCTL_SET_OFF(dev);
    }
}

/*
 * 函数说明
 */
uint8_t fsl_ioctl_is_busy(ioctl_dev_t *dev)
{
    return dev->repeat_cnt != 0;
}

/*
 * 函数说明
 */
#if FSL_IOCTL_LOOP_CALL_MODE == FSL_IOCTL_LOOP_GAP_TICK
void fsl_ioctl_loop(uint8_t loop_gap_ms)
{
    UPDATE_TICK(loop_gap_ms);
#elif FSL_IOCTL_LOOP_CALL_MODE == FSL_IOCTL_LOOP_SYS_TICK
void fsl_ioctl_loop(uint32_t system_tick_ms)
{
    UPDATE_TICK(system_tick_ms);
    
    // 周期控制
    static uint32_t s_tick_start = 0;
    uint32_t tick_now = GET_TICK();
    if ((uint32_t)(tick_now - s_tick_start) < LOOP_PERIOD_MS)
    {
        return;
    }
    s_tick_start = tick_now;
#endif
    
    static list_obj_t* node;
    static ioctl_dev_t *dev;
    list_for_each(node, &dev_list_head)
    {
        dev = list_entry(node, ioctl_dev_t, list);
        if (dev->repeat_cnt == 0) continue;

        if(dev->state == IOCTL_STD_ON)
        {
            if((uint32_t)(GET_TICK() - dev->start_tick) > dev->on_time_cfg)
            {
                IOCTL_SET_OFF(dev);
                if(dev->off_time_cfg) //off time not zero
                { 
                    dev->start_tick = GET_TICK();
                }
                else
                {
                    dev->repeat_cnt = 0;
                }
            }
        }
        else
        {
            if((uint32_t)(GET_TICK() - dev->start_tick) > dev->off_time_cfg)
            {
                if (dev->repeat_cnt != IOCTL_HOLD) dev->repeat_cnt--;

                if(dev->repeat_cnt)
                {
                    IOCTL_SET_ON(dev);
                    dev->start_tick = GET_TICK();
                }
            }
        }
    }
}
